Skip to content

Trim server-loop overhead and add a socket NIF protocol - #139

Merged
lpgauth merged 3 commits into
masterfrom
hot-path-opts
Aug 22, 2026
Merged

Trim server-loop overhead and add a socket NIF protocol#139
lpgauth merged 3 commits into
masterfrom
hot-path-opts

Conversation

@lpgauth

@lpgauth lpgauth commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Summary

Two commits, driven by eprof under load (64 callers, pool of 16, arithmetic test server).

Server loop. The in-flight request queue moves from a shared ETS table into the server state as a map; each request paid two ETS copies plus lock traffic for data only its own process touches. Reply messages now carry just the request id instead of the whole cast record ({shackle_reply, RequestId, Reply} — breaking for anyone matching the raw message instead of using receive_response), and telemetry can be disabled with the telemetry app env. 156-158k -> 167-168k requests/s (+7%).

shackle_socket. A new opt-in protocol built on the socket module. gen_tcp's send path (port_command, port monitor/demonitor, inet_reply round trip, inet_db lookups) was ~32% of server CPU in the profile; socket:send is a single NIF call, and {otp, select_read} gives active-mode-like delivery with one recv per wake — the profile shows 0.41 recvs per request thanks to reply batching. 168-169k -> 172-174k requests/s (+2.5% over shackle_tcp on the same build). Runtime needs OTP 27.3; the module compiles on the whole CI matrix and dialyzer suppressions cover the pre-28 socket specs. Default protocol is unchanged.

Combined: +10% end to end.

Keep the in-flight request queue in the server state as a map
instead of a shared ETS table; each request paid two ETS copies
plus table lock traffic for data only its own process touches.

Reply messages now carry just the request id instead of the whole
cast record, and telemetry can be disabled with the shackle
'telemetry' app env, skipping the handler-table lookup on every
event.

Benchmarked at 64 callers / pool of 16 against the arithmetic test
server: 156-158k -> 167-168k requests/s (+7%).
An alternative to shackle_tcp built on the socket module. Accepted
sockets run with {otp, select_read}, so the server gets one $socket
message plus one recv per wake instead of the inet driver's active
mode delivery, and send is a single NIF call instead of a port
command with its monitor and inet_reply round trip.

Benchmarked at 64 callers / pool of 16 against the arithmetic test
server: 168-169k requests/s with shackle_tcp, 172-174k with
shackle_socket (+2.5%). Requires OTP 27.3 for select_read; the
default protocol is unchanged.
With the JIT and module-wide type propagation these no longer have
a measurable effect: end-to-end benchmarks are within noise with and
without them, while the beams get larger and stack traces lose
inlined frames.
@lpgauth
lpgauth merged commit 4520d61 into master Aug 22, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant